package com.dkm.config;

import java.util.LinkedHashMap;
import java.util.Properties;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 把Subject、SecurityManager、Realm关联起来
 * @author 狄凯明
 *
 */
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import com.dkm.shiro.CustomCredentialsMatcher;
import com.dkm.shiro.CustomRealm;
@Configuration
public class ShiroConfig {
	/**
	 * Shiro自带密码管理器
	 *
	 * @return HashedCredentialsMatcher
	 */
//	@Bean
//	public HashedCredentialsMatcher hashedCredentialsMatcher() {
//		//Shiro自带加密
//	    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//	    //散列算法使用md5
//	    credentialsMatcher.setHashAlgorithmName("md5");
//	    //散列次数，2表示md5加密两次
//	    credentialsMatcher.setHashIterations(2);
//	    credentialsMatcher.setStoredCredentialsHexEncoded(true);
//	    return credentialsMatcher;
//	}

	/**
	 * 1.配置自定义realm
	 * @return
	 */
	@Bean
	public CustomRealm customRealm() {
		CustomRealm customRealm=new CustomRealm();
		//自定义密码管理器
		customRealm.setCredentialsMatcher(new CustomCredentialsMatcher());
		return customRealm;
	}
	/**
	 * 2.配置shiro的核心管理器
	 * @return
	 */
	@Bean
	public SecurityManager securityManager() {
		//shiro的核心管理器对象
		DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
		
		//注入realm
		defaultWebSecurityManager.setRealm(this.customRealm());
		
		return defaultWebSecurityManager;
	}
	
	/**
	 * 3.shiro过滤器
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		//shiro的过滤器对象，拦截请求进行验证
		ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
		
		//注入核心管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
	
		//没有登录配置跳转到登录页面（自定义跳转路径）
		shiroFilterFactoryBean.setLoginUrl("/gologin");
		
		//登录后没有权限时设置跳转路劲
		shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
		
		//配置权限访问链，使用linkedHashMap保证有序
		LinkedHashMap<String, String> filterMap=new LinkedHashMap<String, String>();
		/*
		 * 第一个参数：访问路径，第二个：权限
		 * anon：没有限制都可以访问
		 * authc：必须登录后才能访问
		 * user：Remember Me使用
		 * prems：必须有相应的权限才能操作
		 * roles：必须有相应的角色才能操作
		 */
		filterMap.put("/gologin","anon");
		filterMap.put("/index/login", "anon");
		filterMap.put("/myWebSocket/**", "anon");
		filterMap.put("/communicate/**", "anon");
		
		//除了上面配置的特殊请求的权限外其他的所有请求都需要登录权限，这个配置要放在最下面
		filterMap.put("/**","authc");
		
		//将权限链设置给过滤器对象
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 使用注解设置权限
	 * 解决setUnauthorizedUrl不能跳转的问题
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
	    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
	    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
	    return authorizationAttributeSourceAdvisor;
	}
	
	/**
     * 解决： 无权限页面不跳转 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized") 无效
     * shiro的源代码ShiroFilterFactoryBean.Java定义的filter必须满足filter instanceof AuthorizationFilter，
     * 只有perms，roles，ssl，rest，port才是属于AuthorizationFilter，而anon，authcBasic，auchc，user是AuthenticationFilter，
     * 所以unauthorizedUrl设置后页面不跳转 Shiro注解模式下，登录失败与没有权限都是通过抛出异常。
     * 并且默认并没有去处理或者捕获这些异常。在SpringMVC下需要配置捕获相应异常来通知用户信息
     * @return
     */
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
        //(Properties)import package:java.util
        Properties properties=new Properties();
        //setProperty方法的第二个参数必须改为自己定义的无权限页面路径 
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException","/unauthorized");
        properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","/unauthorized");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }
}
